/*
* Copyright (c) 2002 - 2011
* NetGroup, Politecnico di Torino (Italy)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following condition
* is met:
*
* Neither the name of the Politecnico di Torino nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <pcap.h>
#include "configparams.h"
#include "../utils/utils.h"

#include <nbee.h>
#include <nbnetvm.h>		// We can access to some internal data of the NetVM, so we need also this header

#if defined(_WIN32) || defined(_WIN64)
  #define snprintf _snprintf
#endif

// Global variable for configuration
extern ConfigParams_t ConfigParams;

pcap_t *fpcap = NULL;


void PrintPacketSummary(char *PSMLAsciiBuffer, int PSMLElements);


/*!
\brief This function prints to standard output the list of messages generated by
the PFL compiler during its initialization or during the compilation of a
NetPFL filter.

Based on the provided parameters this function prints out a header message and the result of a
filter compilation phase, followed by a list of warning/error messages generated by the
NetPFL filter compiler

\param	msg		header message to be printed out
\param	result	result of the compilation
\param	msgList	messages list
*/
void PrintCompMessages(const char *msg, int result, _nbNetPFLCompilerMessages *msgList)
{
	printf("%s", msg);
	if (result == nbSUCCESS)
		printf(" succeeded");
	else
		printf(" failed");

	if (msgList == NULL)
	{
		printf(" with no messages\n");
	}
	else
	{
		printf(" with the following messages:\n");
		_nbNetPFLCompilerMessages *next = msgList;
		unsigned int count = 1;
		while (next)
		{
			printf("%3u. %s\n", count++, next->MessageString);
			next = next->Next;
		}
	}
}



/*!
\brief this structure is used for communicating with the NetVM Application callback function

Once processed by the NetVM filter, the accepted packets are sent to a user defined callback function
that prints out a short summary of their content. This structure holds information that the main program
needs to communicate to the callback function
*/
struct DecoderInfo
{
	nbPSMLReader	*PSMLReader;	//!< Reference to the PSML reader
	u_long				*PacketCounter;	//!< Holds the number of packets that reach the callback function (i.e. the ones that have been accepted by the filter)
	nbPacketDecoder *Decoder;		//!< Reference to the Packet Decoder
	pcap_t			*fp;			//!< The actual data-link layer type (needed by the Packet Decoder)

};

/*!
\brief This is the callback function that is called when the NetVM filter accept a packet in Quiet Mode

This function receives a NetVM exchange buffer (containing a network packet) and only increments the
accepted packets counter

\param	xbuffer	reference to the current NetVM exchange buffer being processed
\return nbFAILURE if something goes wrong, nbSUCCESS otherwise
*/

int32_t PrintNothingCallback(nvmExchangeBuffer *xbuffer)
{
	DecoderInfo *info = (DecoderInfo*)xbuffer->UserData;
	u_long *PacketCounter = info->PacketCounter;
	(*PacketCounter)++;

	// Save packets if needed
	if (ConfigParams.SaveFileName)
	{
	struct pcap_pkthdr PktHeader;

	PktHeader.caplen = xbuffer->PacketLen;
	PktHeader.len = xbuffer->PacketLen;
	PktHeader.ts.tv_sec = xbuffer->TStamp_s;
	PktHeader.ts.tv_usec = xbuffer->TStamp_us;

		pcap_dump((unsigned char *) ConfigParams.PcapDumpFile, &PktHeader, xbuffer->PacketBuffer);
	}

	return nbSUCCESS;
}


/*!
\brief This is the callback function that is called when the NetVM filter accept a packet

This function receives a NetVM exchange buffer (containing a network packet) and prints out
to stdout a brief summary of the content of the contained packet, by using the functionalities
provided by the NetBee Packet Decoder.

\param	xbuffer	reference to the current NetVM exchange buffer being processed
\return nbFAILURE if something goes wrong, nbSUCCESS otherwise
*/

int32_t PrintPacketSummaryCallback(nvmExchangeBuffer *xbuffer)
{
	DecoderInfo *info = (DecoderInfo*)xbuffer->UserData;
	u_long *PacketCounter = info->PacketCounter;

	nbPacketDecoder *Decoder = info->Decoder;
	nbPSMLReader *PSMLReader = info->PSMLReader;
	pcap_t *fp = info->fp;
	char *PSMLAsciiBuffer;
	int PSMLElements;
	struct pcap_pkthdr PktHeader;

	PktHeader.caplen = xbuffer->PacketLen;
	PktHeader.len = xbuffer->PacketLen;
	PktHeader.ts.tv_sec = xbuffer->TStamp_s;
	PktHeader.ts.tv_usec = xbuffer->TStamp_us;

	// Decode packet
	if (Decoder->DecodePacket( (nbNetPDLLinkLayer_t) pcap_datalink(fp), *PacketCounter, &PktHeader, xbuffer->PacketBuffer) == nbFAILURE)
	{
		printf("Error decoding a packet %s\n", Decoder->GetLastError());
		return nbFAILURE;
	}

	// Get the current item in PSML format and print it on screen
	PSMLElements= PSMLReader->GetCurrentPacket(&PSMLAsciiBuffer);
	if (PSMLElements == nbFAILURE)
	{
		printf("Reading summary from PSMLReader failed %s\n", PSMLReader->GetLastError());
		return nbFAILURE;
	}

	PrintPacketSummary(PSMLAsciiBuffer, PSMLElements);

	// Save packets if needed
	if (ConfigParams.SaveFileName)
	    pcap_dump((unsigned char *) ConfigParams.PcapDumpFile, &PktHeader, xbuffer->PacketBuffer);

	(*PacketCounter)++;
	return nbSUCCESS;
}


#ifdef __cplusplus
extern "C" {
#endif

	/*!
	\brief This function is used for directly calling the PrintPacketSummaryCallback function
	*/

	int32_t WritePacketFunct(const unsigned char *pkt, u_int32_t pktLen, void *userData, nvmTStamp *tstamp)
	{
		//we must create a fake exchange buffer...
		nvmExchangeBuffer exbuf;
		exbuf.PacketBuffer = (u_int8_t *)pkt;
		exbuf.PacketLen = pktLen;
		exbuf.TStamp_s = tstamp->sec;
		exbuf.TStamp_us = tstamp->usec;
		exbuf.UserData = userData;
		//and pass it to the PrintPacketSummaryCallback function

		if (ConfigParams.QuietMode)
			return PrintNothingCallback(&exbuf);
		else
			return PrintPacketSummaryCallback(&exbuf);
	}

#ifdef __cplusplus
}
#endif


/* We should make a "clean" exit on interrupts */
void cleanupHandler(int Signal)
{
#ifdef HAVE_PCAP_BREAKLOOP
	/*
	 * We have "pcap_breakloop()"; use it, so that we do as little
	 * as possible in the signal handler (it's probably not safe
	 * to do anything with standard I/O streams in a signal handler -
	 * the ANSI C standard doesn't say it is).
	 */
	pcap_breakloop(pd);
#else
	/*
	 * We don't have "pcap_breakloop()"; this isn't safe, but
	 * it's the best we can do.  Print the summary if we're
	 * not reading from a savefile - i.e., if we're doing a
	 * live capture - and exit.
	 */
	if (fpcap != NULL && pcap_file(fpcap) == NULL)
	{
	struct pcap_stat stat;
		/*
		 * We got interrupted, so perhaps we didn't
		 * manage to finish a line we were printing.
		 * Print an extra newline, just in case.
		 */
		if (pcap_stats(fpcap, &stat) < 0)
		{
			printf("Error reading statistics from libpcap/WinPcap: %s\n", pcap_geterr(fpcap));
			exit(0);
		}

		printf("\nPackets filtered: %u, dropped by kernel: %u\n\n", 
			stat.ps_recv, stat.ps_drop);
	}
	exit(0);
#endif
}


/*!
\brief The main program
*/
int main(int argc, char *argv[])
{
	//pcap related structures
	char ErrBuf[PCAP_ERRBUF_SIZE + 1] = "";
	struct pcap_stat stat;
	char CurrentFileName[2024];
	int CurrentFileNumber;
	int DumpFileSize= 0;
	int NetPDLFlags;

	//NetBee related structures
	nbPacketDecoder *Decoder = NULL;
	nbPacketDecoderVars* PacketDecoderVars = NULL;
	nbPSMLReader *PSMLReader = NULL;
	char *PSMLAsciiBuffer = NULL;
	int PSMLElements = 0;

	//PacketEngine related structures
	nbPacketEngine *PacketEngine;

	//NetVM related structures
	char netvmErrBuf[nvmERRBUF_SIZE] = "";
	u_long count = 0;
	nvmRuntimeEnvironment *NetVMRTEnv;

	u_long PacketCounter = 0;

	// Registrer signal handler for Ctrl+C
	signal(SIGINT, &cleanupHandler);

	// whether this tool should return a clean return value or not
	// this is needed because of the many jumps to the "cleanup" label, that is immediately before successful return
	bool returnCleanly = false;

	if (ParseCommandLine(argc, argv) == nbFAILURE)
		return nbFAILURE;

	printf("\nLoading NetPDL protocol database...\n");

	if (ConfigParams.ValidateNetPDL)
		NetPDLFlags= nbPROTODB_FULL | nbPROTODB_VALIDATE;
	else
		NetPDLFlags= nbPROTODB_FULL;

	if (ConfigParams.NetPDLFileName)
	{
	int Res;

		Res= nbInitialize(ConfigParams.NetPDLFileName, NetPDLFlags, ErrBuf, sizeof(ErrBuf) );

		if (Res == nbFAILURE)
		{
			printf("Error initializing the NetBee Library: %s\n", ErrBuf);
			printf("Trying to use the NetPDL database embedded in the NetBee library instead.\n");
		}
	}

	// In case the NetBee library has not been initialized
	// initialize right now with the embedded NetPDL protocol database instead
	// Previous initialization may fail because the NetPDL file has errors, or because it is missing,
	// or because the user didn't specify a NetPDL file
	if (nbIsInitialized() == nbFAILURE)
	{
		if (nbInitialize(NULL, NetPDLFlags, ErrBuf, sizeof(ErrBuf)) == nbFAILURE)
		{
			printf("Error initializing the NetBee Library: %s\n", ErrBuf);
			return nbFAILURE;
		}
	}

	printf("NetPDL Protocol database loaded\n");

	PacketEngine= nbAllocatePacketEngine(ConfigParams.UseJit, ErrBuf, sizeof(ErrBuf));
	if (PacketEngine == NULL)
	{
		fprintf(stderr, "Error retrieving the PacketEngine: %s", ErrBuf);
		return nbFAILURE;
	}
	
	if (ConfigParams.CaptureFileName)
	{
		printf("Opening capture source file...\n");
		// Let's set the source file
		if ((fpcap= pcap_open_offline(ConfigParams.CaptureFileName, ErrBuf)) == NULL)
		{
			printf("Cannot open the capture source file: %s\n", ErrBuf);
			goto cleanup;
		}
		printf("Capture source file opened.\n");
	}
	else
	{
		printf("Opening capture interface...\n");
		// Let's set the device
		if ((fpcap= pcap_open_live(ConfigParams.AdapterName, ConfigParams.SnapLen, ConfigParams.PromiscuousMode, 1000 /* read timeout, ms */, ErrBuf)) == NULL)
		{
			printf("Cannot open the capture interface: %s\n", ErrBuf);
			goto cleanup;
		}
		printf("Capture interface opened.\n");
	}


	if (ConfigParams.FilterString)
	{
		PacketEngine->SetDebugLevel(ConfigParams.DebugLevel);
		PacketEngine->SetNetILCodeFilename(ConfigParams.DumpCodeFilename);

#ifdef	_DEBUG
		PacketEngine->SetHIRCodeFilename(ConfigParams.DumpHIRCodeFilename);
		PacketEngine->SetLIRCodeFilename(ConfigParams.DumpLIRCodeFilename);
		PacketEngine->SetLIRGraphFilename(ConfigParams.DumpLIRGraphFilename);
		PacketEngine->SetLIRNoOptGraphFilename(ConfigParams.DumpLIRNoOptGraphFilename);
		PacketEngine->SetNoCodeGraphFilename(ConfigParams.DumpNoCodeGraphFilename);
		PacketEngine->SetNetILGraphFilename(ConfigParams.DumpNetILGraphFilename);
		PacketEngine->SetProtoGraphFilename(ConfigParams.DumpProtoGraphFilename);
		PacketEngine->SetFilterAutomatonFilename(ConfigParams.DumpFilterAutomatonFilename);
#endif
			
		printf("Compiling filter '%s'...\n", ConfigParams.FilterString);

		int compRes = PacketEngine->Compile(ConfigParams.FilterString,(nbNetPDLLinkLayer_t)pcap_datalink(fpcap), ConfigParams.Backends[0].Optimization);

		if (ConfigParams.DebugLevel > 0)
			PrintCompMessages("\nNetPFL filter compilation", compRes, PacketEngine->GetCompMessageList());

		//---------------------------------
		if (compRes != nbSUCCESS)
		{
		int i=1;

			printf("Unable to compile the filter %s\nError messages:\n", ConfigParams.FilterString);

			_nbNetPFLCompilerMessages *message=PacketEngine->GetCompMessageList();
			while (message != NULL)
			{
				printf("%3d. %s\n", i++, message->MessageString);
				message=message->Next;
			}

			goto cleanup;
		}
	}

	if (ConfigParams.StopAfterDumpCode && ConfigParams.Backends[1].Id==-1)
	{
		if (ConfigParams.DumpCodeFilename==NULL)
		{
			printf("\n\n\nNetIL code\n===== ===== ===== =====\n\n");

			printf("%s", PacketEngine->GetCompiledCode());

			printf("\n===== ===== ===== =====\n");
		}
		else
		{
			FILE *netil = fopen(ConfigParams.DumpCodeFilename, "w");
			if (netil == NULL)
			{
				printf("Unable to open the file %s for writing\n", ConfigParams.DumpCodeFilename);
				goto cleanup;
			}
			fprintf(netil, "%s", PacketEngine->GetCompiledCode());
			fclose(netil);
			printf("NetIL code written to file: %s\n", ConfigParams.DumpCodeFilename);
		}
		return nbSUCCESS;
	}

	printf("\n");

	// Create a NetPDL Parser to decode packet
	Decoder= nbAllocatePacketDecoder(nbDECODER_GENERATEPDML_COMPLETE | nbDECODER_GENERATEPSML, ErrBuf, sizeof(ErrBuf));

	if (Decoder == NULL)
	{
		printf("Error creating the NetPDLParser: %s.\n", ErrBuf);
		goto cleanup;
	}

	// Open the dump file (if needed)
	if (ConfigParams.SaveFileName)
	{
		if (ConfigParams.RotateFiles)
		{
			CurrentFileNumber= 1;

			snprintf(CurrentFileName, sizeof(CurrentFileName)/sizeof(char), "%s%d", ConfigParams.SaveFileName, CurrentFileNumber);
			CurrentFileName[sizeof(CurrentFileName)/sizeof(char) -1 ] = 0;

			ConfigParams.PcapDumpFile= pcap_dump_open(fpcap, CurrentFileName);
		}
		else
			ConfigParams.PcapDumpFile= pcap_dump_open(fpcap, ConfigParams.SaveFileName);

		if (ConfigParams.PcapDumpFile == NULL)
		{
			printf("Error opening dump file: %s\n", pcap_geterr(fpcap));
			goto cleanup;
		}
	}

	// Get the PacketDecoderVars; let's do the check, although it is not really needed
	if ((PacketDecoderVars= Decoder->GetPacketDecoderVars()) == NULL)
	{
		printf("Error: cannot get an instance of the nbPacketDecoderVars class.\n");
		goto cleanup;
	}

	// Set the appropriate NetPDL configuration variables
	PacketDecoderVars->SetVariableNumber((char*) NETPDL_VARIABLE_SHOWNETWORKNAMES, !(ConfigParams.DoNotPrintNetworkNames));

	// Create a new PSML Manager to get data from NetBeePacketDecoder
	PSMLReader= Decoder->GetPSMLReader();
	if (PSMLReader == NULL)
	{
		printf("PSMLReader initialization failed: %s\n", Decoder->GetLastError() );
		goto cleanup;
	}

	PSMLElements= PSMLReader->GetSummary(&PSMLAsciiBuffer);

	if (PSMLElements == nbFAILURE)
	{
		printf("Reading summary from PSMLReader failed %s\n", PSMLReader->GetLastError() );
		goto cleanup;
	}

#ifndef ENABLE_TEST_REPORTS
	DecoderInfo decoderInfo;
	decoderInfo.Decoder = Decoder;
	decoderInfo.fp = fpcap;
	decoderInfo.PacketCounter = &PacketCounter;
	decoderInfo.PSMLReader = PSMLReader;
#endif
	
	if (ConfigParams.FilterString != NULL)
	{
	nbNetVMCreationFlag_t CreationFlag;

		if (ConfigParams.Backends[1].Id >= 0)
			// in this case, you should just compile the code and quit
			CreationFlag= nbNETVM_CREATION_FLAG_COMPILEONLY;
		else
			// in this case, you should compile the code and execute it
			CreationFlag= nbNETVM_CREATION_FLAG_COMPILEANDEXECUTE;

		if ((PacketEngine->InitNetVM(CreationFlag)) == nbFAILURE)
		{
			printf("\n\nNetVM engine initialization failed with the following error:\n%s\n", PacketEngine->GetLastError());
			goto cleanup;
		}

		if (CreationFlag == nbNETVM_CREATION_FLAG_COMPILEONLY)
		{
			if (PacketEngine->GenerateBackendCode(ConfigParams.Backends[1].Id,
											ConfigParams.Backends[1].Optimization,
											ConfigParams.Backends[1].Inline,
											ConfigParams.DumpCodeFilename))
			{
				printf("NetVM Compilation Error: %s\n", PacketEngine->GetLastError());
				goto cleanup;

			}
			if (ConfigParams.DumpCodeFilename != NULL)
			{
			//	printf("Assembly code written to file: %s\n", ConfigParams.DumpCodeFilename);
				goto cleanup;
			}
		}

		// prefix + namepe(netpe0)+.push.s

		if (ConfigParams.StopAfterDumpCode && (ConfigParams.Backends[1].Id >= 0) && (ConfigParams.DumpCodeFilename == NULL))
		{
			printf("\n\n\nBackend code\n===== ===== ===== =====\n\n");

			printf("%s", PacketEngine->GetAssemblyCode());
			printf("\n===== ===== ===== =====\n");

			goto cleanup;
		}

	}

	if (ConfigParams.CaptureFileName == NULL)
		printf("Starting packet capture from interface: %s \n\n", ConfigParams.AdapterName);
	else
		printf("Reading packets from file: %s \n\n", ConfigParams.CaptureFileName);

	if (ConfigParams.QuietMode)
	{
		printf("Capturing in quiet mode...\n");
	}
	else
	{
		PrintPacketSummary(PSMLAsciiBuffer, PSMLElements);
		printf("===============================================================================\n");
	}



#ifdef ENABLE_TEST_REPORTS
        uint64_t starttime, endtime;
        starttime = nbProfilerGetMicro();
#endif

	while (1)
	{
		int RetVal;
		struct pcap_pkthdr *PktHeader;
		const unsigned char *PktData;

		RetVal = pcap_next_ex(fpcap, &PktHeader, &PktData);

		// Capture file ended
		if (RetVal == -2)
		{
			// If we're reading data from a file, let's check if another file
			// with the same name (but a different number at the end) exist on disk
			if (ConfigParams.CaptureFileName)
			{
			char NextCaptureFileName[1024];

				// Let's set the source file
				ssnprintf(NextCaptureFileName, sizeof(NextCaptureFileName), "%s%d", ConfigParams.CaptureFileName, ConfigParams.CaptureFileNumber);

				ConfigParams.CaptureFileNumber++;

				if ((fpcap= pcap_open_offline(NextCaptureFileName, ErrBuf)) == NULL)
					// No other files exist on disk. Let's terminate right now.
					break;
				else
					printf("\nAnother capture source file exists: file '%s' opened.\n\n", NextCaptureFileName);
			}
			continue;
		}

		if (RetVal < 0)
		{
			printf("Cannot read packet: %s\n", pcap_geterr(fpcap));
			return nbFAILURE;
		}

		// Timeout expired
		if (RetVal == 0)
			continue;
			
#ifndef ENABLE_TEST_REPORTS
		//create a nvmTStamp using the timestamp information contained in the pcap packet header
		nvmTStamp ts;
		ts.sec = PktHeader->ts.tv_sec;
		ts.usec = PktHeader->ts.tv_usec;

		if (ConfigParams.FilterString != NULL)
		{
			if(PacketEngine->ProcessPacket(PktData, PktHeader->caplen)==nbSUCCESS)
					WritePacketFunct(PktData, PktHeader->len, &decoderInfo, &ts);
		}
		else
			WritePacketFunct(PktData, PktHeader->len, &decoderInfo, &ts);
#else
		if (ConfigParams.FilterString != NULL)
		{
			if(PacketEngine->ProcessPacket(PktData, PktHeader->caplen)==nbSUCCESS)
					PacketCounter++;
		}
		else
			PacketCounter++;

#endif

		count++;

		// Check if the user wanted to capture max N packets
		if ((ConfigParams.NPackets != 0) && (PacketCounter == ConfigParams.NPackets))
			break;

		if ((ConfigParams.RotateFiles) && (ConfigParams.SaveFileName))
		{
			DumpFileSize+= sizeof(struct pcap_pkthdr) + PktHeader->caplen;

			if (DumpFileSize >= ConfigParams.RotateFiles)
			{
				DumpFileSize= 0;
				CurrentFileNumber++;

				snprintf(CurrentFileName, sizeof(CurrentFileName)/sizeof(char), "%s%d", ConfigParams.SaveFileName, CurrentFileNumber);
				CurrentFileName[sizeof(CurrentFileName)/sizeof(char) -1] = 0;

				pcap_dump_close(ConfigParams.PcapDumpFile);
				ConfigParams.PcapDumpFile= pcap_dump_open(fpcap, CurrentFileName);

				if (ConfigParams.PcapDumpFile == NULL)
				{
					printf("Error opening dump file: %s\n", pcap_geterr(fpcap));
					goto cleanup;
				}
			}
		}

	}

#ifdef ENABLE_TEST_REPORTS
        endtime = nbProfilerGetMicro();
        printf("start:%lu end:%lu diff:%lu\n",
               starttime, endtime, endtime-starttime);
        printf("AKA %lu pkt/s\n", count*1000000/(endtime-starttime));
#endif

	if(ConfigParams.CaptureFileName == NULL)
	{
		if (pcap_stats(fpcap, &stat) < 0)
		{
			printf("Error reading statistics from libpcap/WinPcap: %s\n", pcap_geterr(fpcap));
			goto cleanup;
		}

		printf("\nPackets read: %lu, accepted: %lu, filtered: %lu, dropped by kernel: %u\n\n", 
			count, PacketCounter, count-PacketCounter, stat.ps_drop);
	}
	
	NetVMRTEnv= PacketEngine->GetNetVMRuntimeEnvironment();
	nvmPrintStatistics(NetVMRTEnv);
	if (nvmRuntimeHasStats())
	{
		nvmNetPEHandlerStats *stats = nvmGetPEHandlerStats(NetVMRTEnv, netvmErrBuf);
		if (stats == NULL)
		{
			printf("Error retrieving runtime stats: %s", netvmErrBuf);
			goto cleanup;
		}
	
		while (stats)
		{
			if (stats->NumPkts != 0)	//let's avoid division by 0!
			{
				printf("PE %s, %s handler: %u packets processed, %u packets forwarded, %llu ticks total, %llu ticks average\n", \
					stats->PEName, stats->HandlerName, stats->NumPkts, stats->NumPktsFwd, (long long unsigned int) stats->NumTicks, (long long unsigned int) stats->NumTicks/stats->NumPkts);
			}
			else
			{
				printf("PE %s, %s handler: %u packets processed, %llu ticks total\n", \
					stats->PEName, stats->HandlerName, stats->NumPkts, (long long unsigned int) stats->NumTicks);
			}
			stats = stats->Next;
		}
	}

	returnCleanly = true;

cleanup:
	if (Decoder)
		delete Decoder;

	nbCleanup();

	return (returnCleanly? nbSUCCESS : nbFAILURE);
}


// Print the ascii elements contained in the PSML buffer
void PrintPacketSummary(char *PSMLAsciiBuffer, int PSMLElements)
{
	int BufferPtr;

	BufferPtr= 0;

	for (int i= 0; i < PSMLElements; i++)
	{
		int PrintedChars;

		PrintedChars= printf("%s", &PSMLAsciiBuffer[BufferPtr]);
		BufferPtr = BufferPtr + PrintedChars + 1;

		// Print a separator between items
		printf("\t");
	}

	printf("\n");
}
